#define WIDTH 60
#define HEIGHT 25
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winuser.h>
#include <time.h>
#include <conio.h>

int HIGH_SCORE;
int SCORE;
int SPEED = 200;
int SCORE_PER_FOOD;
int game_over;

enum MOVE
{
    UP,
    DOWN,
    LEFT,
    RIGHT,
};

typedef struct
{
    int x;
    int y;
} Position;

Position snake[HEIGHT * WIDTH];
int length;
Position food;
enum MOVE direction;

void gen_food(void);
void init(void);
void speed_up(void);
void speed_down(void);

void init()
{
    game_over = 0;
    SCORE = 0;
    length = 3;
    direction = RIGHT;
    SCORE_PER_FOOD = 1;

    snake[0].x = WIDTH / 2;
    snake[0].y = HEIGHT / 2;

    snake[1].x = WIDTH / 2 - 1;
    snake[1].y = HEIGHT / 2;

    snake[2].x = WIDTH / 2 - 2;
    snake[2].y = HEIGHT / 2;

    gen_food();
}

void gen_food()
{
    srand(time(0));

RETRY:
    food.x = rand() % WIDTH;
    food.y = rand() % HEIGHT;
    int flag = 0;

    for (size_t i = 0; i < length; i++)
    {
        if (food.x == snake[i].x && food.y == snake[i].y)
        {
            goto RETRY;
        }
    }
    if (flag == 1)
    {
        gen_food();
    }
}

void draw()
{
    system("cls");

    for (int i = 0; i < WIDTH + 2; i++)
        printf("#");
    printf("\n");

    for (int y = 0; y < HEIGHT; y++)
    {
        for (int x = 0; x < WIDTH; x++)
        {
            if (x == 0)
                printf("#");

            if (x == snake[0].x && y == snake[0].y)
                printf("O");

            else
            {
                int isBody = 0;
                for (int k = 1; k < length; k++)
                {
                    if (x == snake[k].x && y == snake[k].y)
                    {
                        printf("o");
                        isBody = 1;
                        break;
                    }
                }

                if (!isBody && x == food.x && y == food.y)
                    printf("F");
                else if (!isBody)
                    printf(" ");
            }

            if (x == WIDTH - 1)
                printf("#");
        }
        printf("\n");
    }

    for (int i = 0; i < WIDTH + 2; i++)
        printf("#");
    printf("\n");

    printf("Score: %d\n", SCORE);
    printf("High Score: %d\n", HIGH_SCORE);
}

void input()
{
    if (GetAsyncKeyState(VK_UP) & 0x8000 && direction != DOWN)
    {
        direction = UP;
    }
    else if (GetAsyncKeyState(VK_DOWN) & 0x8000 && direction != UP)
    {
        direction = DOWN;
    }
    else if (GetAsyncKeyState(VK_LEFT) & 0x8000 && direction != RIGHT)
    {
        direction = LEFT;
    }
    else if (GetAsyncKeyState(VK_RIGHT) & 0x8000 && direction != LEFT)
    {
        direction = RIGHT;
    }

    Sleep(1);

    if (GetAsyncKeyState(VK_F1) & 0x8000)
    {
        speed_up();
    }
    else if (GetAsyncKeyState(VK_F2) & 0x8000)
    {
        speed_down();
    }
}

void move_snake()
{

    for (int i = length - 1; i > 0; i--)
    {
        snake[i] = snake[i - 1];
    }

    switch (direction)
    {
    case UP:
        snake[0].y--;
        break;
    case DOWN:
        snake[0].y++;
        break;
    case LEFT:
        snake[0].x--;
        break;
    case RIGHT:
        snake[0].x++;
        break;
    }

    if (snake[0].x >= WIDTH || snake[0].x < 0 || snake[0].y >= HEIGHT || snake[0].y < 0)
        game_over = 1;

    for (int i = 1; i < length; i++)
    {
        if (snake[0].x == snake[i].x && snake[0].y == snake[i].y)
            game_over = 1;
    }

    if (snake[0].x == food.x && snake[0].y == food.y)
    {
        gen_food();

        speed_up();
        length++;
        SCORE += SCORE_PER_FOOD;
    }
}

void speed_up()
{
    if (SPEED > 10)
    {
        SPEED -= 10;

        if (SPEED == 320)
        {
            SCORE_PER_FOOD = 2;
        }
        else
        {
            SCORE_PER_FOOD += 2;
        }
    }
}

void speed_down()
{
    if (SPEED < 500)
    {
        SPEED += 30;

        if (SPEED == 350)
        {
            SCORE_PER_FOOD = 1;
        }
        else
        {
            SCORE_PER_FOOD = (SCORE_PER_FOOD > 2) ? (SCORE_PER_FOOD - 2) : 1;
        }
    }
}

void print_snake_figure()
{
    FILE *file = fopen("snakeFigure.txt", "r");
    if (file == NULL)
    {
        perror("Cannot open file");
    }

    char line[1024];
    while (fgets(line, sizeof(line), file) != NULL)
    {
        printf("%s", line);
    }
    fclose(file);
}

void explain()
{
    system("cls");
    print_snake_figure();
    printf("\n");

    printf("A classic snake game. Use up,down,left,right to control   \n");
    printf("Press any key to continue...\n");

    int a;
    scanf("%d", &a);
}

int read_high_score()
{
    FILE *file = fopen("save.txt", "r");
    int value;
    value = 0;

    if (file == NULL)
    {
        file = fopen("save.txt", "w");
        if (file == NULL)
        {
            fprintf(file, "0");
        }
        return 0;
    }
    else
    {
        if (fscanf(file, "%d", &value) != 1)
        {
            value = 0;
            fclose(file);
            file = fopen("save.txt", "w");
            fprintf(file, "0");
        }
    }

    fclose(file);
    return value;
}

void show_high_score()
{
    system("cls");
    print_snake_figure();
    printf("\n");
    printf("Highscore :   %d\n", HIGH_SCORE);
    printf("Press any key to continue...\n");

    int a;
    scanf("%d", &a);
}

int check_input()
{
    int input;
    while (1)
    {
        if (scanf("%d", &input) != 1)
        {
            printf("Invalid Option! Please retry\n");
            while (getchar() != '\n')
                continue;
        }
        else if (input >= 1 && input <= 4)
        {
            break;
        }
        else
        {
            printf("Invalid Option! Please retry\n");
        }
    }

    return input;
}

int welcome_to_game()
{

    int status;
    system("cls");

    print_snake_figure();
    status = check_input();

    switch (status)
    {
    case 1:
        return 0;
    case 2:
        explain();
        return 1;
    case 3:
        show_high_score();
        return 1;
    case 4:
        exit(0);
    }

    return 1;
}

int game_over_menu()
{

    system("cls");
    printf("\n");
    printf("  ___     _     __  __   ___      ___   __   __  ___   ___ ");
    printf("\n");
    printf("  / __|   /_\\   |  \\/  | | __|    / _ \\  \\ \\ / / | __| | _ \\");
    printf("\n");
    printf(" | (_ |  / _ \\  | |\\/| | | _|    | (_) |  \\ V /  | _|  |   /");
    printf("\n");
    printf("  \\___| /_/ \\_\\ |_|  |_| |___|    \\___/    \\_/   |___| |_|_\\");
    printf("\n");

    printf("SCORE: %d\n", SCORE);
    if (SCORE > HIGH_SCORE)
    {
        printf("创纪录啦！最高分被你刷新啦，真棒！！！\n");
        FILE *file = fopen("save.txt", "w");
        if (file != NULL)
        {
            fprintf(file, "%d", SCORE);
            fclose(file);
            HIGH_SCORE = SCORE;
        }
    }
    else
    {
        printf("继续努力吧~你离最高分还差: %d\n", HIGH_SCORE - SCORE);
    }

    int choice;
    while (1)
    {
        printf("\n1. Restart\n");
        printf("2. Exit\n");

        if (scanf("%d", &choice) != 1)
        {
            printf("输入错误，请重新选择！\n");
            while (getchar() != '\n')
                ;
            continue;
        }

        if (choice == 1 || choice == 2)
        {
            break;
        }
        else
        {
            printf("输入错误，请重新选择！\n");
        }
    }

    return (choice == 1) ? 0 : 1;
}

int main()
{
    system("chcp 65001");
    HIGH_SCORE = read_high_score();

    while (welcome_to_game())
    {
        continue;
    }

RESTART:
    init();
    while (!game_over)
    {
        draw();
        Sleep(SPEED);

        input();
        move_snake();
    }

    if (game_over_menu() == 1)
    {
        exit(0);
    }
    else
    {
        goto RESTART;
    }

    return 0;
}
